package entities;

import java.util.Observable;

import datatypes.Coordinate;
import datatypes.CoordinatePrecise;
import datatypes.MCAction;
import datatypes.MCAnimation;


@SuppressWarnings("unused") //TODO: Remove when all meta-data is implemented
public abstract class Entity extends Observable {
	protected int eid;
	
	
	private Coordinate lastlocation;
	private CoordinatePrecise location;
	
	private float yaw = 0;
	private float pitch = 0;
	
	private MCAnimation anim = MCAnimation.NONE;
	
	
	
	/*
	 * Common Metadata
	 */
	private boolean onFire = false;
	private boolean crouching = false;
	private boolean riding = false;
	private boolean sprinting = false;
	private boolean doingItemAction = false;
	
	private short drowningcounter = 300;
	private int potioneffects = 0; // color of bubbling effectings around the player = 4 bit nibbles?

	
	//TODO: Move to animals
	//private int babycounter = 0;
	
	
	
	public Entity(int eid) {
		this.eid = eid;
		System.out.println("Setting eid: "+eid);
		
		this.location = new CoordinatePrecise(5550, 34, 5550);
		this.lastlocation = location.toAbsCoordinate();
	}
	
	public int geteid() {
		return eid;
	}
	
	protected void updateMe(EntityEvent event) {
		setChanged();
		notifyObservers(event);
	}
	
	
	
	/****
	 * Entity animation
	 */
	public void setAnimation(MCAnimation animation) {
		this.anim = animation;
		updateMe(EntityEvent.ANIMATIONCHANGED);
		this.anim = MCAnimation.NONE;
	}

	
	
	/*****
	 * Entity action
	 */
	public void setAction(MCAction action) {

		if(action == MCAction.STARTSPRINT) {
			this.sprinting = true;
		} else if(action == MCAction.STOPSPRINT) {
			this.sprinting = false;
		} else if(action == MCAction.CROUCH) {
			this.crouching = true;
		} else if(action == MCAction.UNCROUCH) {
			this.crouching = false;
		} else if(action == MCAction.LEAVEBED) {
			//TODO
			System.out.println("TODO: Leavebed implementation.");
		}
				
		updateMe(EntityEvent.ENTITYACTION);

	}
	
	
	/******
	 * Entity Location 
	 */
	public void setLocation(double x, double y, double z) {
		location = new CoordinatePrecise(x, y, z);
	}
	
	public CoordinatePrecise getLocation() {
		return location;
	}
	
	/**
	 * Return the last location sent to all clients.
	 * I.e. This is the location all clients are aware of since last movement update.
	 * @return
	 */
	public Coordinate getLastLocation() {
		return lastlocation;
	}
	
	/**
	 * The current players location.
	 * @return
	 */
	public CoordinatePrecise location() {
		return location;
	}
	
	/**
	 * To be called when the location of a player/entity has been changed
	 * in such a way that other players/entities need to be informed.
	 * 
	 * After calling this method, it is assumed that all clients know the
	 * new location.
	 */
	public synchronized void updateLocation() {
		updateMe(EntityEvent.LOCATIONUPDATED);
		lastlocation = location.toAbsCoordinate();
	}
	
	
	
	/**
	 * Set the entities pitch.
	 * @param pitch
	 */
	public void setPitch(float pitch) {
		this.pitch = pitch;
	}
	
	/**
	 * Sets the entities yaw.
	 * @param yaw
	 */
	public void setYaw(float yaw) {
		this.yaw = yaw;
	}
	
	
	public float getPitch() {
		return pitch;
	}
	
	public float getYaw() {
		return yaw;
	}
	

	public MCAnimation getAnimation() {
		return anim;
	}
	
	
	public byte[] getMetaDataBytes() {
		byte infobyte = (byte) (0x01 & (this.onFire?1:0));		
		infobyte += (0x02 & (this.crouching?0x02:0));
		infobyte += (0x04 & (this.riding?0x04:0));
		infobyte += (0x08 & (this.sprinting?0x08:0));
		infobyte += (0x10 & (this.doingItemAction?0x10:0));
		
		byte[] metadata = new byte[2];
		metadata[0] = 0;					//byte & infoblock-id 0
		metadata[1] = infobyte;
		
		//TODO: Support drowning			byte[0] = 0x01 << 5 + 0x01  //short & infoblock-id 1
		//TODO: Support Potion Effects		byte[0] = 0x02 << 5 + 0x01  //int   & infoblock-id 8
		
		return metadata;
	}
	
	
    /**
     * Returns a hashcode for the Entity. Since each entity should
     * have a unique ID, we will just emit this ID as hashcode.
     */
    @Override
	public int hashCode() {
    	return eid;
    }
    
    
    /**
     * When an entity has the same eid, consider it to be equal.
     * (This corresponds to the hashCode method implementation.)
     */
    public boolean equals(Object other) {
    	if(other instanceof Entity && ((Entity) other).geteid() ==  this.geteid()) {
    		return true;
    	} else {
    		return false;
    	}
    }
	
	
}
